package cn.sh.tools.service.component.jvm;

import cn.hutool.core.util.StrUtil;
import cn.sh.tools.domain.other.Student;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * @Description jvm
 * @Classname JvmComponent
 * @Date 2023/6/13
 * @Author sh
 **/
@Component
@Slf4j
public class JvmComponent {

    public static final int multiple = 100;

    public static final String SH_ESCAPE_ANALYSIS_WAY_FIRST = "1";

    /**
     * 逃逸分析
     */
    public void escapeAnalysis(int createStudentCount) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < createStudentCount; i++) {
            allocStudent();
        }
        // 查看执行时间
        log.info("cost {} ms", (System.currentTimeMillis() - start));

        /**
         * 测试 在栈上为对象分配内存，有两种方式：
         * 1. 使用 jmap -histo vmid（java进程id） 查看堆内存中对象个数
         *    这种测试方式的前提：
         *      ① 前提是要将 Java堆内存设置的足够大，因为设置的太小的话，当 Java堆中 user对象 变少时，
         *        就无法确定到底是因为 开启逃逸分析 导致的，还是因为 gc 导致的。
         *      ② 为了方便查看，就需要下面的代码（线程 sleep），留下足够的时间可以通过 jmap命令查看。
         * 1.1 先测试 关闭逃逸分析 的情况
         *      当控制台打印出 cost XX ms 时，使用 jmap 命令查看 Java堆中有多少个 User 对象了。
         * 1.2 再测试 开启逃逸分析 的情况
         *      当控制台打印出 cost XX ms 时，使用 jmap 命令查看 Java堆中有多少个 User 对象了。
         *      理论上来说，当开启逃逸分析后，大多数对象都会在栈上分配内存，所以，Java堆中的 user 对象应该就会变少。
         *      ① -XX:+DoEscapeAnalysis，开启逃逸分析。② -XX:+EliminateAllocations，开启标量替换。③ -XX:+PrintGCDetails 打印gc日志详情。
         *
         * 2. 将 Java堆内存设置的小一点，配置启动参数 -XX:+PrintGCDetails 打印GC日志。
         *      理论情况下来讲，开启逃逸分析 比 关闭逃逸分析 发生的GC次数要少的多，因为大多数对象都会在栈上分配内存，只有少数对象在堆上分配内存。
         *    这里的结论中只说【少的多】，是因为 HotSpot虚拟机的执行引擎包含了 解释器、即时编译器。而逃逸分析技术是即时编译器的优化技术。
         */
        String shEscapeAnalysisWay = System.getProperty("shEscapeAnalysisWay");
        if (StrUtil.isNotBlank(shEscapeAnalysisWay)
                && SH_ESCAPE_ANALYSIS_WAY_FIRST.equals(shEscapeAnalysisWay)) {
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
        }
    }

    private static void allocStudent() {
        Student user = new Student();
        user.setName("ABC");
        user.setAge(1000);
    }

    public Integer visualVmBtrace(Integer countOfCycles) {
        List<Integer> resultList = new ArrayList<>(countOfCycles);
        for (int i = 0; i < countOfCycles; i++) {
            int a = (int) Math.round(Math.random() * multiple);
            int b = (int) Math.round(Math.random() * multiple);
            resultList.add(this.add(a, b));
        }
        return resultList.stream().reduce(Integer::sum).orElse(0);
    }

    private Integer add(int a, int b) {
        return a + b;
    }

}
